DID中地址聚合器的实现 | Move dApp 极速入门(八)
在本文中我们将介绍 DID 合约 @Starcoin 的 AddrAggregator (地址聚合器)部分的实现。
仓库地址:
https://github.com/NonceGeek/DID-Solution-In-Move
本文包括如下知识点:
资源的概念与使用 合约操 签名算法 数据的增删改查 Starcoin 控制台的使用
0x01 准备工作
首先,按照如何设置本地开发网络中的描述启动一个开发网络,并获得一些 STC。
在本文档中,我将使用我的开发网络的默认帐户地址 0xa8db68190dc89c14e387e1bfb454d088
来创建一个did合约。
创建自定义 did 的代码源文件位于 did。
1.1 环境准备
1、下载最新版本的starcoin安装包,把解压后的目录配置到环境变量里
下载地址:
https://github.com/starcoinorg/starcoin/releases
2、打开一个控制台,启动节点
starcoin -n dev
3、打开一个新的控制台,启动交互式环境
starcoin -n dev console
1.2 账号准备
# 新建两个账号一个用来部署合约以及初始化合约,另一个用来调用did合约相关方法
starcoin% account create -p 123456
# 这里生成账号(用于部署合约):0xa8db68190dc89c14e387e1bfb454d088
# 通过控制台给账号充钱
starcoin% dev get-coin [acct you get from create cmd] -v 2000STC
1.3 DID科普
DID 是由w3c
推出的数字身份协议, Decentralized identifiers (DIDs) 是是一种新型标识符,可实现可验证的去中心化数字身份。具体可查阅did文档
DID标识符:
did = "did:" 方法名称 ":" 方法特定 ID; 例子:did:example:id
1.4 AddrAggregator 功能分析
写一个给不同地址分配的ID的合约,这里简化了DID定义,对于地址对应的是一个ID,实际上就是一个地址聚合器 主要包含功能有:
初始化地址聚合器
添加地址,生成地址对象,产生唯一 ID,获取当前区块高度作为待签名的信息,记录创建时间
更新地址对象的签名以及时间
删除地址
0x02 合约实现
代码总共包含4个合约, AddrAggregator是DID处理聚合器、EthSigVerifierV5是eth签名验证,Util是公共方法的封装
这里主要分析AddrAggregator合约,这也是did的核心
定义AddrInfo,包含地址详细信息
struct AddrInfo has store, copy, drop {
addr: address, //地址
description: vector<u8>, //描述
chain_name: vector<u8>, //链名
msg: vector<u8>, //msg信息
signature: vector<u8>, //签名
created_at: u64, //创建时间
updated_at: u64, //更新时间
id : u64, //id
}
定义AddrAggregator,包含AddrInfo的集合、最大ID
struct AddrAggregator has key {
key_addr: address,
addr_infos: vector<AddrInfo>, //地址信息集合
max_id : u64 //当前最大id
}
AddrAggregator中的函数:初始化AddrAggregator
public (script) fun create_addr_aggregator(sender: signer){
//定义AddrAggregator结构
let addr_aggr = AddrAggregator{
key_addr: Signer::address_of(&sender),
addr_infos: Vector::empty<AddrInfo>(),
max_id : 0
};
//把AddrAggregator资源移动到sender的账户下
move_to<AddrAggregator>(&sender, addr_aggr);
}
添加地址
public (script) fun add_addr(sender: signer,
addr: address,
chain_name: vector<u8>,
description: vector<u8>) acquires AddrAggregator {
//获取AddrAggregator资源
let addr_aggr = borrow_global_mut<AddrAggregator>(Signer::address_of(&sender));
//max_id加1
let id = addr_aggr.max_id + 1;
//获取区块高度
let height = Block::get_current_block_number();
let msg = Utils::u64_to_vec_u8(height);
let now = Timestamp::now_seconds();
//记录创建时间、height作为addr_info的msg
let addr_info = AddrInfo{
addr: addr,
chain_name: chain_name,
description: description,
signature: x"",
msg: msg,
created_at: now,
updated_at: 0,
id : id,
};
//把addr_info加入addr_infos中
Vector::push_back(&mut addr_aggr.addr_infos, addr_info);
//addr_aggr的max_id加1
addr_aggr.max_id = addr_aggr.max_id + 1;
}
获取地址的待签名信息
public fun get_msg(contract: address, addr: address) :vector<u8> acquires AddrAggregator {
//获取AddrAggregator资源
let addr_aggr = borrow_global_mut<AddrAggregator>(contract);
let length = Vector::length(&mut addr_aggr.addr_infos);
let i = 0;
while (i < length) {
let addr_info = Vector::borrow<AddrInfo>(&mut addr_aggr.addr_infos, i);
//地址匹配
if (addr_info.addr == addr) {
//返回待签名信息
return *&addr_info.msg
};
};
return x""
}
更新地址签名
public (script) fun update_addr_with_sig(sender: signer,
addr: address, signature : vector<u8>) acquires AddrAggregator {
//获取AddrAggregator资源
let addr_aggr = borrow_global_mut<AddrAggregator>(Signer::address_of(&sender));
let length = Vector::length(&mut addr_aggr.addr_infos);
let i = 0;
while (i < length) {
let addr_info = Vector::borrow_mut<AddrInfo>(&mut addr_aggr.addr_infos, i);
//地址匹配
if (addr_info.addr == addr) {
//空msg判断
if (*&addr_info.msg == x"") {
abort 1001
};
// verify the signature for the msg
// kecacak256哈希运算
let msg_hash = Hash::keccak_256(*&addr_info.msg); //kecacak256 hash
if (!EthSigVerifierV5::verify_eth_sig(copy signature, addr, msg_hash)) {
abort 1002
};
// verify the now - created_at <= 2h
//时限判断
let now = Timestamp::now_seconds();
if (now - addr_info.created_at > 2*60*60) {
abort 1003
};
// update signature, updated_at
//更新签名,时间
addr_info.signature = signature;
addr_info.updated_at = now;
break
};
i = i + 1;
};
}
删除地址
public (script) fun delete_addr(
sender: signer,
addr: address) acquires AddrAggregator{
//获取AddrAggregator资源
let addr_aggr = borrow_global_mut<AddrAggregator>(Signer::address_of(&sender));
let length = Vector::length(&mut addr_aggr.addr_infos);
let i = 0;
while (i < length) {
let addr_info = Vector::borrow(&mut addr_aggr.addr_infos, i);
//地址匹配
if (addr_info.addr == addr) {
//从addr_infos删除地址
Vector::remove(&mut addr_aggr.addr_infos, i);
break
};
i = i + 1;
}
}
0x03 合约部署
修改 Move.toml
文件中 SNFT
地址为默认账户地址:
[addresses]
StarcoinFramework = "0x1"
MyAddr = "0xa8db68190dc89c14e387e1bfb454d088"
在 shell 控制台中运行 mpm release
以获取发布模块:
$ mpm release
Packaging Modules:
0xa8db68190dc89c14e387e1bfb454d088::Utils
0xa8db68190dc89c14e387e1bfb454d088::EndpointAggregatorV5
0xa8db68190dc89c14e387e1bfb454d088::EthSigVerifierV5
0xa8db68190dc89c14e387e1bfb454d088::AddrAggregator
Release done: release/did.v0.0.1.blob, package hash: 0x624a7e37094eeae02411d7991ca61b973a76b8a5908823c424ea73c694db46ff
它将编译合约代码,在项目的 release
目录下生成二进制文件 did.v0.0.1.blob
,此二进制文件将用于之后的部署。
解锁帐户并部署 did 合约:
starcoin% account unlock 0xa8db68190dc89c14e387e1bfb454d088 -p <MY-PASSWORD>
starcoin% dev deploy /path/to/did/release/did.v0.0.1.blob -s 0xa8db68190dc89c14e387e1bfb454d088 -b
txn 0xda09b71180b3b2ba924d8e983c13a3415b2b2ff6f5eeb0f8d53ed4458c872eba submitted.
{
"ok": {
"dry_run_output": {
"events": [],
"explained_status": "Executed",
"gas_used": "28760",
"status": "Executed",
.....
....
}
可以看到交易已经提交,结果状态为 Executed
。这意味着该模块已部署。
0x04 执行脚本功能
4.1 初始化
首先调用create_addr_aggregator进行初始化。
account execute-function --function 0xa8db68190dc89c14e387e1bfb454d088::AddrAggregator::create_addr_aggregator -s 0xa8db68190dc89c14e387e1bfb454d088 -b
调用资源查看命令,可以看到addr_infos为空,max_id为0。
state get resource 0xa8db68190dc89c14e387e1bfb454d088 0xa8db68190dc89c14e387e1bfb454d088::AddrAggregator::AddrAggregator
返回:
{
"ok": {
"json": {
"addr_infos": [
],
"key_addr": "0xa8db68190dc89c14e387e1bfb454d088",
"max_id": 0
},
"raw": "0xa8db68190dc89c14e387e1bfb454d088"
}
}
4.2 增加地址
调用add_addr增加地址:
account execute-function --function 0xa8db68190dc89c14e387e1bfb454d088::AddrAggregator::add_addr -s 0xa8db68190dc89c14e387e1bfb454d088 --arg x"14791697260E4c9A71f18484C9f997B308e59325" --arg b"ethereum" --arg b"test_addr"
返回Executed,调用成功。
调用资源查看命令,可以看到addr_infos已经有值,max_id为1, 从返回结果可以看出,addr_infos已经有了0x14791697260e4c9a71f18484c9f997b308e59325的地址相关信息,包括chain_name, description, msg, id, created_at等信息。
state get resource 0xa8db68190dc89c14e387e1bfb454d088 0xa8db68190dc89c14e387e1bfb454d088::AddrAggregator::AddrAggregator
返回:
{
"ok": {
"json": {
"addr_infos": [
{
"addr": "0x14791697260e4c9a71f18484c9f997b308e59325",
"chain_name": "0x657468",
"created_at": 36,
"description": "0x65746861646472",
"id": 1,
"msg": "0x372e6e6f6e63655f6765656b",
"signature": "0x",
"updated_at": 0
}
],
"key_addr": "0xa8db68190dc89c14e387e1bfb454d088",
"max_id": 1
},
"raw": "0xa8db68190dc89c14e387e1bfb454d088011414791697260e4c9a71f18484c9f997b308e593250765746861646472036574680104002400000000000000000000000000000001000000000000000100000000000000"
}
}
获取特定地址的msg信息,用作后面签名适用的待签名信息。
dev call --function 0xa8db68190dc89c14e387e1bfb454d088::AddrAggregator::get_msg --arg 0xa8db68190dc89c14e387e1bfb454d088 --arg x"14791697260E4c9A71f18484C9f997B308e59325"
返回:
{
"ok": [
"0x372e6e6f6e63655f6765656b"
]
}
记得要将 hex binary 做一下 Base16.decode,得到待签名字符串:
4.3 用 eth.build 签名
通过 eth.build,我们可以容易地使用 ethereum 的签名算法对信息进行签名。
https://eth.build/build#62a34ce54c284dc5dd430b1edf767ecca405a8b58e55797be3ed114c964138d6
可以使用私钥直接签,也可以调起 Metamask 进行签名。
4.4 给地址添加签名
更新地址的签名(拿到上一步的待签名信息,计算签名后作为参数传入)。
account execute-function --function 0xa8db68190dc89c14e387e1bfb454d088::AddrAggregator::update_addr_with_sig -s 0xa8db68190dc89c14e387e1bfb454d088 --arg x"14791697260E4c9A71f18484C9f997B308e59325" --arg x"e02fcfd6e2f9d7a1aa6b12cef95bb6e5013b14dc584e080034b09f902fd87d8b4e46d83462ae957f8db86f50aac61dde24261befd1c11780adc8e0d5ee4e34971c"
返回 Executed,调用成功。
调用资源查看命令,可以看到14791697260E4c9A71f18484C9f997B308e59325的signature和updated_at字段已经更新:
state get resource 0xa8db68190dc89c14e387e1bfb454d088 0xa8db68190dc89c14e387e1bfb454d088::AddrAggregator::AddrAggregator
返回:
{
"ok": {
"json": {
"addr_infos": [
{
"addr": "0x14791697260e4c9a71f18484c9f997b308e59325",
"chain_name": "0x657468",
"created_at": 36,
"description": "0x65746861646472",
"id": 1,
"msg": "0x04",
"signature": "0xe02fcfd6e2f9d7a1aa6b12cef95bb6e5013b14dc584e080034b09f902fd87d8b4e46d83462ae957f8db86f50aac61dde24261befd1c11780adc8e0d5ee4e34971c",
"updated_at": 43
}
],
"key_addr": "0xa8db68190dc89c14e387e1bfb454d088",
"max_id": 1
},
"raw": "0xa8db68190dc89c14e387e1bfb454d088011414791697260e4c9a71f18484c9f997b308e59325076574686164647203657468010441e02fcfd6e2f9d7a1aa6b12cef95bb6e5013b14dc584e080034b09f902fd87d8b4e46d83462ae957f8db86f50aac61dde24261befd1c11780adc8e0d5ee4e34971c24000000000000002b0000000000000001000000000000000100000000000000"
}
}
调用delete_addr删除地址。
account execute-function --function 0xa8db68190dc89c14e387e1bfb454d088::AddrAggregator::delete_addr -s 0xa8db68190dc89c14e387e1bfb454d088 --arg x"14791697260E4c9A71f18484C9f997B308e59325"
返回 Executed,调用成功。
此时再次调用资源查看命令,可以看到addr_infos为空,max_id为1,符合预期。
state get resource 0xa8db68190dc89c14e387e1bfb454d088 0xa8db68190dc89c14e387e1bfb454d088::AddrAggregator::AddrAggregator
返回:
{
"ok": {
"json": {
"addr_infos": [],
"key_addr": "0xa8db68190dc89c14e387e1bfb454d088",
"max_id": 1
},
"raw": "0xa8db68190dc89c14e387e1bfb454d088000100000000000000"
}
}
4.5 练习
0x01)目前添加地址的方式还存在安全风险,你知道是哪方面的安全风险吗?
0x02)目前只能添加符合 ethereum 格式规范的地址,请补充一个函数,让其能添加 ed25519(starcoin)的地址。
答案回复链接:
https://github.com/NonceGeek/Web3-dApp-Camp/discussions/136